package app

import (
	"context"
	"errors"
	"fmt"
	"log"
	"log/slog"
	"os"
	"os/signal"
	"syscall"
	"time"

	"gitverse.ru/dlc/similarity-compiler/internal/config"
	"gitverse.ru/dlc/similarity-compiler/internal/initialize"
	"gitverse.ru/dlc/similarity-compiler/pkg/closer"
	"gitverse.ru/dlc/similarity-compiler/pkg/compilers/luajit"
)

var ErrPanicRecovered = errors.New("recovered panic on application run")

const (
	shutdownTimeout = 5 * time.Second
)

func Run(config *config.Config) (appErr error) {
	defer func() {
		if recovered := recover(); recovered != nil {
			appErr = ErrPanicRecovered

			log.Printf("recovered panic on application run: %v", recovered)
		}
	}()

	ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
	defer cancel()

	logger := slog.New(slog.NewTextHandler(os.Stdout, nil))
	closer := closer.New()

	/// ================================

	luajitCompiler, err := luajit.New(luajit.Deps{
		LuaJITBinPath: config.Service.Compilers.LuaJitBinary,
	})
	if err != nil {
		return fmt.Errorf("initialize luajit compiler: %w", err)
	}

	services, err := initialize.NewServices(initialize.ServicesDeps{
		LuaJITCompiler: luajitCompiler,
	})
	if err != nil {
		return fmt.Errorf("initialize services: %w", err)
	}

	if err := setupGRPC(grpcDeps{
		Logger:   logger,
		Closer:   closer,
		Config:   config,
		Services: services,
	}); err != nil {
		return err.Wrap("setup grpc")
	}

	/// ================================

	<-ctx.Done()

	logger.Info("shutting down app gracefully")

	shutdownCtx, cancel := context.WithTimeout(context.Background(), shutdownTimeout)
	defer cancel()

	if err := closer.Close(shutdownCtx); err != nil {
		return fmt.Errorf("execute closer: %w", err)
	}

	return nil
}
